#include <vector>
#include <string>

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "CommonTests.h"

#include "IterRange.h"
#include "WhereRange.h"

#include "ToContainer.h"

using namespace boolinq;
    

//////////////////////////////////////////////////////////////////////////

TEST(WhereRange, IntOdd)
{
    int src[] = {1,2,3,4,5,6};
    int ans[] = {1,  3,  5};

    auto rng = range(src);
    auto dst = where(rng, [](int a){return a%2 == 1;});
    
    CheckRangeEqArray(dst, ans);
}

//////////////////////////////////////////////////////////////////////////

TEST(Where_iRange, IntOdd)
{
    int src[] =   {1,2,3,4,5,6};
    int ans1[] = {      4,5,6};
    int ans2[] = {      4,  6};

    auto rng = range(src);
    auto dst1 = where_i(rng, [](int /*a*/, int idx){return idx>2;});
    auto dst2 = where_i(rng, [](int a, int idx){return idx>2 && a%2 == 0;});
   
    CheckRangeEqArray(dst1, ans1);
    CheckRangeEqArray(dst2, ans2);
}

//////////////////////////////////////////////////////////////////////////

TEST(WhereRange, FirstLetterFront)
{
    std::string src[] =
    {
        "apple",
        "blackberry",
        "adobe",
        "microsoft",
        "nokia",
    };

    std::string ans[] =
    {
        "apple",
        "adobe",
    };

    auto rng = range(src);
    auto dst = where(rng, [](std::string a){return a[0] == 'a';});
    
    CheckRangeEqArray(dst, ans);
}

//////////////////////////////////////////////////////////////////////////

TEST(Where_iRange, FirstLetterFront)
{
    std::string src[] =
    {
        "apple",
        "blackberry",
        "adobe",
        "microsoft",
        "nokia",
    };

    std::string ans[] =
    {
        "adobe",
    };

    auto rng = range(src);
    auto dst = where_i(rng, [](std::string a, int idx){return a[0] == 'a' && idx > 0;});
    
    CheckRangeEqArray(dst, ans);
}

//////////////////////////////////////////////////////////////////////////

TEST(WhereRange, NameAgeLess)
{
    struct NameAge
    {
        std::string name;
        int age;
    };

    NameAge src[] =
    {
        {"man1",20},
        {"man2",15},
        {"man3",30},
        {"man4",14},
        {"man5",18},
    };

    NameAge ans[] =
    {
        {"man2",20},
        {"man4",15},
    };

    auto rng = range(src);
    auto dst = where(rng, [](const NameAge & a){return a.age < 18;});
    
    CheckRangeEqArray(dst, ans, [](const NameAge & a){return a.name;});
}

//////////////////////////////////////////////////////////////////////////

TEST(Where_iRange, NameAgeLess)
{
    struct NameAge
    {
        std::string name;
        int age;
    };

    NameAge src[] =
    {
        {"man1",20},
        {"man2",15},
        {"man3",30},
        {"man4",14},
        {"man5",18},
    };

    NameAge ans[] =
    {
        {"man3",30},
        {"man5",18},
    };

    auto rng = range(src);
    auto dst = where_i(rng, [](const NameAge & a, int idx){return a.age > 15 && idx >= 2;});
    
    CheckRangeEqArray(dst, ans, [](const NameAge & a){return a.name;});
}

//////////////////////////////////////////////////////////////////////////

TEST(WhereRange, ManyToOne)
{
    int src[] = {0,1,2};
    int ans[] = {1};
    
    auto rng = range(src);
    auto dst = where(rng, [](int a){return a == 1;});

    CheckRangeEqArray(dst, ans);
}

//////////////////////////////////////////////////////////////////////////

TEST(Where_iRange, ManyToOne)
{
    int src[] = {0,1,2};
    int ans1[] = {1};
    int ans2[] = {2};
    
    auto rng = range(src);
    auto dst1 = where_i(rng, [](int a, int idx){return a > 0 && idx == 1;});
    auto dst2 = where_i(rng, [](int /*a*/, int idx){return idx == 2;});

    CheckRangeEqArray(dst1, ans1);
    CheckRangeEqArray(dst2, ans2);
}

//////////////////////////////////////////////////////////////////////////

TEST(WhereRange, OneToOne)
{
    int src[] = {5};
    int ans[] = {5};

    auto rng = range(src);
    auto dst = where(rng, [](int a){return a>0;});

    CheckRangeEqArray(dst, ans);
}

//////////////////////////////////////////////////////////////////////////

TEST(Where_iRange, OneToOne)
{
    int src[] = {5};
    int ans[] = {5};

    auto rng = range(src);
    auto dst = where_i(rng, [](int /*a*/, int idx){return idx == 0;});

    CheckRangeEqArray(dst, ans);
}

//////////////////////////////////////////////////////////////////////////

TEST(WhereRange, ManyToZero)
{
    int src[] = {0,1,2};
    
    auto rng = range(src);
    auto dst = where(rng, [](int a){return a == 5;});

    EXPECT_TRUE(dst.empty());
}

//////////////////////////////////////////////////////////////////////////

TEST(Where_iRange, ManyToZero)
{
    int src[] = {0,1,2};
    
    auto rng = range(src);
    auto dst = where_i(rng, [](int /*a*/, int idx){return idx >2;});
    auto dst2 = where_i(rng, [](int a, int idx){return idx%2 == 1 && a != 1;});

    EXPECT_TRUE(dst.empty());
    EXPECT_TRUE(dst2.empty());
}

//////////////////////////////////////////////////////////////////////////

TEST(WhereRange, OneToZero)
{
    int src[] = {5};
    
    auto rng = range(src);
    auto dst = where(rng, [](int a){return a>10;});

    EXPECT_TRUE(dst.empty());
}

//////////////////////////////////////////////////////////////////////////

TEST(Where_iRange, OneToZero)
{
    int src[] = {5};
    
    auto rng = range(src);
    auto dst = where_i(rng, [](int /*a*/, int idx){return idx != 0;});

    EXPECT_TRUE(dst.empty());
}

//////////////////////////////////////////////////////////////////////////

TEST(WhereRange, ZeroToZero)
{
    std::vector<int> src;

    auto rng = range(src);
    auto dst = where(rng, [](int a){return a>0;});

    EXPECT_TRUE(rng.empty());
}

//////////////////////////////////////////////////////////////////////////

TEST(Where_iRange, ZeroToZero)
{
    std::vector<int> src;

    auto rng = range(src);
    auto dst = where_i(rng, [](int /*a*/, int idx){return idx > 0;});

    EXPECT_TRUE(rng.empty());
}

//////////////////////////////////////////////////////////////////////////
